﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TFDP.Common;
using Microsoft.Data.Schema.Sql.SchemaModel;
using Microsoft.VisualStudio.Data.Schema.Package.Project;
using Microsoft.Data.Schema.SchemaModel;
using TFDP.Common.T4;
using System.IO;
using System.ComponentModel.Composition;
using TFDP.Common.Configuration;

namespace TFDP.Processing
{
    [Export(typeof(IGenerator))]
    public class TriggerGenerator : IGenerator
    {
        private IProcessor processor;
        private IDatabaseProjectNode project;

        #region IGenerator Members

        public Type SupportedType
        {
            get { return typeof(ISqlTable); }
        }

        public void Initialize(IProcessor processor, IDatabaseProjectNode project)
        {
            this.processor = processor;
            this.project = project;

            DatabaseTemplateProcessor.Instance.TemplateDirectory = project.FindTemplateDirectory();
        }

        public IEnumerable<GeneratedArtifact> Generate(IModelElement element)
        {
            List<GeneratedArtifact> retvals = new List<GeneratedArtifact>();

            if (element.Node(project).ItemProperty(LoodswezenProperties.GenerateTriggersProperty))
            {
                ISqlTable table = element as ISqlTable;
                bool hasSystemColumns = table.HasSystemColumns();

                // Check for system columns
                if (!hasSystemColumns && !table.HasCustomTriggerContent())
                    return retvals;

                GeneratedArtifact item = null;

                // Insert Trigger
                if (hasSystemColumns || table.HasCustomTriggerContent("Insert"))
                {
                    item = GenerateTrigger(element, "Insert", String.Empty, "InsertTrigger.tt");

                    if (item != null)
                        retvals.Add(item);
                }

                // Update Trigger
                if (hasSystemColumns || table.HasCustomTriggerContent("Update"))
                {
                    item = GenerateTrigger(element, "Update", String.Empty, "UpdateTrigger.tt");

                    if (item != null)
                        retvals.Add(item);
                }

                // Delete Trigger
                // Check if there is a history table present,
                // if not we don't need to generate a delete trigger
                // unless there is custom trigger content
                if (table.HasHistoryTable() || table.HasCustomTriggerContent("Delete"))
                {
                    item = GenerateTrigger(element, "Delete", String.Empty, "DeleteTrigger.tt");

                    if (item != null)
                        retvals.Add(item);
                }
            }

            return retvals;
        }

        #endregion

        private GeneratedArtifact GenerateTrigger(IModelElement element, string triggerPrefix, string triggerSuffix, string templateFile)
        {
            ISqlTable sourceTable = element as ISqlTable;
            if (sourceTable == null)
                throw new ArgumentException("Invalid type", "element");

            String elementName = String.Format("{0}.{1}{2}", element.Name.Parts[0], element.Name.Parts[1], triggerPrefix);

            Logger.LogVerbose("Generating trigger " + elementName);

            // Check if the procedure exists
            ISqlTrigger trigger = project.FindEntity<ISqlTrigger>(elementName);

            // Obtain the properties of the element
            IDatabaseNode node = element.Node(this.project);

            // Execute the template
            string buff = ProcessTemplate<ISqlTable>(templateFile, sourceTable, node);

            // If the output of ProcessTemplate is null or String.Empty
            // there was a problem with the template and we're finished
            if (String.IsNullOrEmpty(buff))
                return null;

            // Determine the path
            string path = (trigger == null) ? ComposeTriggerPath(element.Name, triggerPrefix, triggerSuffix) : trigger.PrimarySource.SourceName;

            // Write output to temporary file
            string tempFile = Path.GetTempFileName();
            File.WriteAllText(tempFile, buff);

            // Return the GenerationItem
            return new GeneratedArtifact(tempFile, path, trigger);
        }

        private string ProcessTemplate<T>(string templateFile, T element, IDatabaseNode node) where T : IModelElement
        {
            return DatabaseTemplateProcessor.Instance.GenerateWithTemplate<T>(templateFile, element, node);
        }

        private string ComposeTriggerPath(ModelIdentifier tableIdentifier, string triggerPrefix, string triggerSuffix)
        {
            string format = "{0}\\Schema Objects\\Schemas\\{1}\\Tables\\Triggers\\{2}{3}{4}.trigger.sql";

            if (processor.Settings.ContainsKey("TriggerPathFormat"))
            {
                string triggerPathFormat = processor.Settings.Get<string>("TriggerPathFormat");
                if(!String.IsNullOrEmpty(triggerPathFormat))
                    format = triggerPathFormat;
            }

            return String.Format(
                format,
                project.DirectoryName,
                tableIdentifier.Parts[0],
                tableIdentifier.Parts[1], // Tablename
                triggerPrefix,
                triggerSuffix
            );
        }
    }
}
